/*
 * @Author: yayalee yangcheng960813@163.com
 * @Date: 2024-05-16 22:41:25
 * @LastEditTime: 2024-07-12 11:38:13
 * @LastEditors: yayalee yangcheng960813@163.com
 * @Description: 
 * @FilePath: \unfinished-challenges\assets\core_ts\DateUtil.ts
 * 可以输入预定的版权声明、个性签名、空行等
 */

import { StringUtil } from "./StringUtil";
import { isNumber, isString } from "./TsUtil";

export class DateUtil {

    private static _formatNormalize(formatter: any) {
        if (typeof formatter === "function") return formatter;
        if (typeof formatter !== "string")
            throw new TypeError("formatter must be a string");
        if (formatter === "date") {
            formatter = "yyyy-MM-dd";
        } else if (formatter === "datetime") {
            formatter = "yyyy-MM-dd HH:mm:ss";
        }
        const formatterFunc = (dateInfo: any) => {
            const { yyyy, MM, dd, HH, mm, ss, ms, w, q, a, A } = dateInfo;
            return formatter
                .replace("YYYY", yyyy)
                .replace("yyyy", yyyy)
                .replace("MM", MM)
                .replace("DD", dd)
                .replace("dd", dd)
                .replace("HH", HH)
                .replace("hh", HH)
                .replace("mm", mm)
                .replace("ss", ss)
                .replace("ms", ms)
                .replace("w", w)
                .replace("q", q)
                .replace("a", a)
                .replace("A", A)

        }
        return formatterFunc;
    }

    /**
    * @description: 
    * @param {any} formatter "2023/6/7 12:30:40:33"
    * @return {*}
    * @author: yayalee
    * @since: v1.0.0
    */
    public static formatterStrDate(formatter?: any): Date {
        if (isNumber(formatter)) {
            return new Date(formatter);
        }
        if (formatter instanceof Date) {
            return formatter;
        }
        if (isString(formatter)) {
            formatter = formatter.replace(/-/g, "/")
            return new Date(Date.parse(formatter));
        }
        return new Date();
    }

    /**
     * 
     * @param formatter 
     * @param date 
     * @param isPad 
     * @returns 
     */
    public static formatterDate(formatter?: string | ((dataInfo: any) => string), date?: number | string | Date | any, isPad = true): string {
        if (typeof date === "number" || typeof date === "string") date = new Date(date);
        formatter = DateUtil._formatNormalize(formatter);
        const dateInfo = {
            year: date.getFullYear(),
            month: date.getMonth() + 1,
            day: date.getDate(),
            hour: date.getHours(),
            minute: date.getMinutes(),
            second: date.getSeconds(),
            week: date.getDay(),
            millisecond: date.getMilliseconds(),
            q: Math.floor((date.getMonth() + 3) / 3),//季度
            a: date.getHours() < 12 ? "上午" : "下午",//上下午
            A: date.getHours() < 12 ? "AM" : "PM",//AM/PM
        } as any;
        const weekDay = ["日", "一", "二", "三", "四", "五", "六"];
        dateInfo.yyyy = dateInfo.year.toString();
        dateInfo.MM = dateInfo.month.toString();
        dateInfo.dd = dateInfo.day.toString();
        dateInfo.HH = dateInfo.hour.toString();
        dateInfo.hh = dateInfo.hour.toString();
        dateInfo.mm = dateInfo.minute.toString();
        dateInfo.ss = dateInfo.second.toString();
        dateInfo.ms = dateInfo.millisecond.toString();
        dateInfo.w = weekDay[dateInfo.week];
        function _pad(prop: string, len: number) {
            dateInfo[prop] = StringUtil.padStr(dateInfo[prop], len, "0");
        }
        if (isPad) {
            _pad("yyyy", 4);
            _pad("MM", 2);
            _pad("dd", 2);
            _pad("HH", 2);
            _pad("hh", 2);
            _pad("mm", 2);
            _pad("ss", 2);
            _pad("ms", 3);
        }
        //@ts-ignore
        const result = formatter(dateInfo);
        return result;
    }

    /**
    * 对一段时间返回格式化时间字符串
    * @param sec 时间s
    * @param format 格式化字符串
    * @example
    * // 当format为string时，会以format中的最大时间单位进行格式化
    * _js.formatTimeString(3601, "%{m}:%{s}"); // 60:1
    * _js.formatTimeString(3601, "%{mm}:%{ss}"); // 60:01
    * _js.formatTimeString(3601, "%{hh}:%{mm}:%{ss}"); // 01:00:01
    * 
    * // 当format为object时，会以传入的sec计算最大的时间单位，并选择format对应的字符串进行格式化
    * _js.formatTimeString(100, {
    *     S: "%{s}秒",
    *     M: "%{m}分%{s}秒",
    *     H: "%{h}时%{m}分%{s}秒",
    *     D: "%{d}天%{h}时%{m}分%{s}秒"
    * }); // 1分40秒
    * _js.formatTimeString(100000, {
    *     S: "%{s}秒",
    *     M: "%{m}分%{s}秒",
    *     H: "%{h}时%{m}分%{s}秒",
    *     D: "%{d}天%{h}时%{m}分%{s}秒"
    * }); // 1天3时46分40秒
    */
    public static formatTimeString(sec: number, format: string | { "S": string; "M": string; "H": string; "D": string } = "%{hh}:%{mm}:%{ss}"): string {
        let seconds: number = Math.floor(sec);
        let minutes: number = Math.floor(seconds / 60);
        let hours: number = Math.floor(seconds / 3600);
        let days: number = Math.floor(seconds / 86400);

        enum TimeUnit {
            S,
            M,
            H,
            D
        }

        let maxUnit: TimeUnit = TimeUnit.S;
        let result: string = "";

        if (typeof format === "string") {
            // 查询格式化字符串中最大的单位
            result = format;
            if (/%\{d+\}/.test(format)) {
                maxUnit = TimeUnit.D;
            } else if (/%\{h+\}/.test(format)) {
                maxUnit = TimeUnit.H;
            } else if (/%\{m+\}/.test(format)) {
                maxUnit = TimeUnit.M;
            }
        } else {
            // 以传入的数值判断最大单位
            if (days > 0) {
                maxUnit = TimeUnit.D;
                result = format.D;
            } else if (hours > 0) {
                maxUnit = TimeUnit.H;
                result = format.H;
            } else if (minutes > 0) {
                maxUnit = TimeUnit.M;
                result = format.M;
            } else {
                maxUnit = TimeUnit.S;
                result = format.S;
            }
        }

        if (maxUnit > TimeUnit.S) {
            seconds %= 60;
        }
        if (maxUnit > TimeUnit.M) {
            minutes %= 60;
        }
        if (maxUnit > TimeUnit.H) {
            hours %= 24;
        }
        let data = {
            dd: StringUtil.padStr(days.toString(), 2, "0"),
            d: `${days}`,
            hh: StringUtil.padStr(hours.toString(), 2, "0"),
            h: `${hours}`,
            mm: StringUtil.padStr(minutes.toString(), 2, "0"),
            m: `${minutes}`,
            ss: StringUtil.padStr(seconds.toString(), 2, "0"),
            s: `${seconds}`
        };
        result = StringUtil.formatString(result, data);
        return result;
    }

}